package com.itbaizhan.clouddemooauth2server.config;

import com.itbaizhan.Constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;


/**
 * 认证授权配置类
 */

@Configuration
@EnableAuthorizationServer //开启认证服务器的功能
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager  authenticationManager;

    //DataSource在配置类中已配置好了, 直接注入就行了
    @Autowired
    private DataSource dataSource;


    /**
     * 认证授权服务器以接口调用的方式对外提供服务,因涉及到接口访问权限问题, 接口的安全配置写在这个方法里
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        security.allowFormAuthenticationForClients() //开启客户端表单认证
                .tokenKeyAccess("permitAll()")  //开启端口/oauth/token的访问权限(允许所有)
                .checkTokenAccess("permitAll()"); //开启端口/oauth/check_token的访问权限(允许所有)
    }

    /**
     * 加载客户信息, 可以写死在方法里, 也可以通过数据库查询客户信息
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//         clients.inMemory()  //客户端信息写在内存中
//        .withClient("itbaizhan_cloud_demo") //添加一个client_id, 并指定一个名称
//        .secret("{noop}abcde")  //客户端密码, {noop}代表不进行加密
//        .resourceIds("cloud-demo-product", "cloud-demo-order")  //资源id, 微服务名
//        .authorizedGrantTypes("password", "refresh_token")  //令牌颁发的方式, 采用密码式
//        .scopes("all");  //权限范围

        //从数据库加载客户端信息
        clients.withClientDetails(jdbcClientDetailsService());
    }

    private ClientDetailsService jdbcClientDetailsService() {
        JdbcClientDetailsService detailsService = new JdbcClientDetailsService(dataSource);
        return detailsService;
    }

    /**
     * 配置令牌的属性, 例如: 令牌的有效期, 令牌的存储方式
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.tokenStore(tokenStore())  //指定token的存储方法
        .tokenServices(authorizationServerTokenServices()) //token生成细节描述, 比如: token的有效期
        .authenticationManager(authenticationManager)   //通过注入的方式授权管理器对象
        .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); //请求的方法类型
    }


    /**
     * 此方法获取令牌服务对象(它描述了token的有效期等信息)
     * @return
     */
    private AuthorizationServerTokenServices authorizationServerTokenServices() {

        //使用默认的实现类
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        //是否开启刷新令牌
        defaultTokenServices.setSupportRefreshToken(true);
        //设置令牌的存储方式
        defaultTokenServices.setTokenStore(tokenStore());
        //针对jwt令牌的增强
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
        //设置令牌的有效期
        defaultTokenServices.setAccessTokenValiditySeconds(7200);  //access_token
        //设置刷新令牌的有效期
        defaultTokenServices.setRefreshTokenValiditySeconds(84400);
        return defaultTokenServices;
    }

    /**
     *  返回令牌转换器(jwt令牌), 签名密钥传给转换器
     * @return
     */
    private JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(Constant.sign_key); //签名密钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(Constant.sign_key));  //验证时使用的密钥, 和签名密钥一样
        return jwtAccessTokenConverter;
    }


    /**
     * 此方法用于创建令牌存储对象
     * @return
     */
    private TokenStore tokenStore() {
        //将令牌保存在内存中
        // return new InMemoryTokenStore();
        //将令牌保存在数据库中
        //return new JdbcTokenStore(dataSource);
        //使用jwt
        return new JwtTokenStore(jwtAccessTokenConverter());
    }


}
